/**
 * ScriptDev3 is an extension for mangos providing enhanced features for
 * area triggers, creatures, game objects, instances, items, and spells beyond
 * the default database scripting in mangos.
 *
 * Copyright (C) 2006-2013  ScriptDev2 <http://www.scriptdev2.com/>
 * Copyright (C) 2014-2019  MaNGOS  <https://getmangos.eu>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * World of Warcraft, and all World of Warcraft or Warcraft art, images,
 * and lore are copyrighted by Blizzard Entertainment, Inc.
 */

/**
 * ScriptData
 * SDName:      Eastern_Plaguelands
 * SD%Complete: 100
 * SDComment:   Quest support: 7622.
 * SDCategory:  Eastern Plaguelands
 * EndScriptData
 */

/**
 * ContentData
 * npc_eris_havenfire
 * EndContentData
 */

#include "precompiled.h"

/*######
## npc_eris_havenfire
######*/

enum
{
    SAY_PHASE_HEAL                      = -1000815,
    SAY_EVENT_END                       = -1000816,
    SAY_EVENT_FAIL_1                    = -1000817,
    SAY_EVENT_FAIL_2                    = -1000818,
    SAY_PEASANT_APPEAR_1                = -1000819,
    SAY_PEASANT_APPEAR_2                = -1000820,
    SAY_PEASANT_APPEAR_3                = -1000821,

    // SPELL_DEATHS_DOOR                 = 23127,           // damage spells cast on the peasants
    // SPELL_SEETHING_PLAGUE             = 23072,
    SPELL_ENTER_THE_LIGHT_DND           = 23107,
    SPELL_BLESSING_OF_NORDRASSIL        = 23108,

    NPC_INJURED_PEASANT                 = 14484,
    NPC_PLAGUED_PEASANT                 = 14485,
    NPC_SCOURGE_ARCHER                  = 14489,
    NPC_SCOURGE_FOOTSOLDIER             = 14486,
    NPC_THE_CLEANER                     = 14503,            // can be summoned if the priest has more players in the party for help. requires further research

    QUEST_BALANCE_OF_LIGHT_AND_SHADOW   = 7622,

    MAX_PEASANTS                        = 13,
    MAX_ARCHERS                         = 8,
    MAX_PEASANTS_TO_SAVE                = 50,
    MAX_PEASANTS_TO_DIE                 = 15,
};

static const float aArcherSpawn[8][4] =
{
    {3327.42f, -3021.11f, 170.57f, 6.01f},
    {3335.4f,  -3054.3f,  173.63f, 0.49f},
    {3351.3f,  -3079.08f, 178.67f, 1.15f},
    {3358.93f, -3076.1f,  174.87f, 1.57f},
    {3371.58f, -3069.24f, 175.20f, 1.99f},
    {3369.46f, -3023.11f, 171.83f, 3.69f},
    {3383.25f, -3057.01f, 181.53f, 2.21f},
    {3380.03f, -3062.73f, 181.90f, 2.31f},
};

static const float aPeasantSpawnLoc[3] = {3360.12f, -3047.79f, 165.26f};
static const float aPeasantMoveLoc[3] = {3335.0f, -2994.04f, 161.14f};

static const int32 aPeasantSpawnYells[3] = {SAY_PEASANT_APPEAR_1, SAY_PEASANT_APPEAR_2, SAY_PEASANT_APPEAR_3};

struct npc_eris_havenfire : public CreatureScript
{
    npc_eris_havenfire() : CreatureScript("npc_eris_havenfire") {}

    struct npc_eris_havenfireAI : public ScriptedAI
    {
        npc_eris_havenfireAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); }

        uint32 m_uiEventTimer;
        uint32 m_uiSadEndTimer;
        uint8 m_uiPhase;
        uint8 m_uiCurrentWave;
        uint8 m_uiKillCounter;
        uint8 m_uiSaveCounter;

        ObjectGuid m_playerGuid;
        GuidList m_lSummonedGuidList;

        void Reset() override
        {
            m_uiEventTimer = 0;
            m_uiSadEndTimer = 0;
            m_uiPhase = 0;
            m_uiCurrentWave = 0;
            m_uiKillCounter = 0;
            m_uiSaveCounter = 0;

            m_playerGuid.Clear();
            m_lSummonedGuidList.clear();
            m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER);
        }

        void JustSummoned(Creature* pSummoned) override
        {
            switch (pSummoned->GetEntry())
            {
            case NPC_INJURED_PEASANT:
            case NPC_PLAGUED_PEASANT:
                {
                    float fX, fY, fZ;
                    pSummoned->GetRandomPoint(aPeasantMoveLoc[0], aPeasantMoveLoc[1], aPeasantMoveLoc[2], 10.0f, fX, fY, fZ);
                    pSummoned->GetMotionMaster()->MovePoint(1, fX, fY, fZ);
                }
                break;
            case NPC_SCOURGE_FOOTSOLDIER:
            case NPC_THE_CLEANER:
                if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid))
                {
                    pSummoned->AI()->AttackStart(pPlayer);
                }
                break;
            case NPC_SCOURGE_ARCHER:    // these should attack the peasants - who are not spawned yet
                //{
                //    std::vector<ObjectGuid> peasantGUID;
                //    for (GuidList::const_iterator it = m_lSummonedGuidList.begin(); it != m_lSummonedGuidList.end(); ++it)
                //    {
                //        if ((*it).GetEntry() == NPC_INJURED_PEASANT || (*it).GetEntry() == NPC_PLAGUED_PEASANT)
                //        {
                //            if (Creature* peasant = m_creature->GetMap()->GetCreature(*it))
                //                if (peasant->IsAlive() && pSummoned->IsInRange(peasant, 5.0f, 30.0f))
                //                    peasantGUID.push_back(*it);
                //        }
                //    }
                //    ObjectGuid victimGUID = peasantGUID[urand(0, peasantGUID.size() - 1)];
                //    if (Creature* victim = m_creature->GetMap()->GetCreature(victimGUID))
                //        pSummoned->AI()->AttackStart(victim);   // ot Creature::Attack(victim, false) ?
                //}
                break;
            }

            m_lSummonedGuidList.push_back(pSummoned->GetObjectGuid());
        }

        void SummonedMovementInform(Creature* pSummoned, uint32 uiMotionType, uint32 uiPointId) override
        {
            if (uiMotionType != POINT_MOTION_TYPE || uiPointId != 1)
            {
                return;
            }

            ++m_uiSaveCounter;
            pSummoned->GetMotionMaster()->Clear();

            pSummoned->RemoveAllAuras();
            pSummoned->CastSpell(pSummoned, SPELL_ENTER_THE_LIGHT_DND, false);
            pSummoned->ForcedDespawn(10000);
            m_lSummonedGuidList.remove(pSummoned->GetObjectGuid());

            // Event ended
            if (m_uiSaveCounter >= MAX_PEASANTS_TO_SAVE)
            {
                DoBalanceEventEnd();
            }
            // Phase ended
            else if (m_uiSaveCounter + m_uiKillCounter == m_uiCurrentWave * MAX_PEASANTS)
            {
                DoHandlePhaseEnd();
            }
        }

        void SummonedCreatureJustDied(Creature* pSummoned) override
        {
            if (pSummoned->GetEntry() == NPC_INJURED_PEASANT || pSummoned->GetEntry() == NPC_PLAGUED_PEASANT)
            {
                ++m_uiKillCounter;

                // If more than 15 peasants have died, then fail the quest
                if (m_uiKillCounter >= MAX_PEASANTS_TO_DIE)
                {
                    if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid))
                    {
                        pPlayer->FailQuest(QUEST_BALANCE_OF_LIGHT_AND_SHADOW);
                    }

                    DoScriptText(SAY_EVENT_FAIL_1, m_creature);
                    m_uiSadEndTimer = 1*IN_MILLISECONDS;
                    m_uiEventTimer = 0;
                }
                else if (m_uiSaveCounter + m_uiKillCounter == m_uiCurrentWave * MAX_PEASANTS)
                {
                    DoHandlePhaseEnd();
                }
            }
            m_lSummonedGuidList.remove(pSummoned->GetObjectGuid());
        }

        void DoSummonWave(uint32 uiSummonId = 0)
        {
            float fX, fY, fZ;

            switch (uiSummonId)
            {
            case NPC_SCOURGE_FOOTSOLDIER:
                {
                    uint8 uiRand = urand(2, 3);
                    for (uint8 i = 0; i < uiRand; ++i)
                    {
                        m_creature->GetRandomPoint(aPeasantSpawnLoc[0], aPeasantSpawnLoc[1], aPeasantSpawnLoc[2], 15.0f, fX, fY, fZ);
                        m_creature->SummonCreature(NPC_SCOURGE_FOOTSOLDIER, fX, fY, fZ, 0, TEMPSUMMON_DEAD_DESPAWN, 0);
                    }
                }
                break;
            case NPC_SCOURGE_ARCHER:
                for (uint8 i = 0; i < MAX_ARCHERS; ++i)
                    m_creature->SummonCreature(NPC_SCOURGE_ARCHER, aArcherSpawn[i][0], aArcherSpawn[i][1], aArcherSpawn[i][2], aArcherSpawn[i][3], TEMPSUMMON_DEAD_DESPAWN, 0);
                break;
            default:
                for (uint8 i = 0; i < MAX_PEASANTS; ++i)
                {
                    uint32 uiSummonEntry = roll_chance_i(70) ? NPC_INJURED_PEASANT : NPC_PLAGUED_PEASANT;
                    m_creature->GetRandomPoint(aPeasantSpawnLoc[0], aPeasantSpawnLoc[1], aPeasantSpawnLoc[2], 10.0f, fX, fY, fZ);
                    if (Creature* pTemp = m_creature->SummonCreature(uiSummonEntry, fX, fY, fZ, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 10*MINUTE*IN_MILLISECONDS))
                    {
                        // Only the first mob needs to yell
                        if (!i)
                            DoScriptText(aPeasantSpawnYells[urand(0, 2)], pTemp);
                    }
                }

                ++m_uiCurrentWave;
                break;
            }
        }

        void DoHandlePhaseEnd()
        {
            if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid))
            {
                pPlayer->CastSpell(pPlayer, SPELL_BLESSING_OF_NORDRASSIL, true);
            }

            DoScriptText(SAY_PHASE_HEAL, m_creature);

            // Send next wave
            if (m_uiCurrentWave < 5)
            {
                DoSummonWave();
            }
        }

        void DoStartBalanceEvent(Player* pPlayer)
        {
            m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER);
            m_playerGuid = pPlayer->GetObjectGuid();
            m_uiEventTimer = 5000;
        }

        void DoBalanceEventEnd()
        {
            if (Player* pPlayer = m_creature->GetMap()->GetPlayer(m_playerGuid))
            {
                pPlayer->AreaExploredOrEventHappens(QUEST_BALANCE_OF_LIGHT_AND_SHADOW);
            }

            DoScriptText(SAY_EVENT_END, m_creature);
            DoDespawnSummons(true);
            EnterEvadeMode();
        }

        void DoDespawnSummons(bool bIsEventEnd = false)
        {
            for (GuidList::const_iterator itr = m_lSummonedGuidList.begin(); itr != m_lSummonedGuidList.end(); ++itr)
            {
                if (bIsEventEnd && ((*itr).GetEntry() == NPC_INJURED_PEASANT || (*itr).GetEntry() == NPC_PLAGUED_PEASANT))
                    continue;

                if (Creature* pTemp = m_creature->GetMap()->GetCreature(*itr))
                    if (pTemp->IsAlive())
                        pTemp->ForcedDespawn();
            }
        }

        void UpdateAI(const uint32 uiDiff) override
        {
            if (m_uiEventTimer)
            {
                if (m_uiEventTimer <= uiDiff)
                {
                    switch (m_uiPhase)
                    {
                    case 0:
                        DoSummonWave(NPC_SCOURGE_ARCHER);
                        m_uiEventTimer = 5000;
                        break;
                    case 1:
                        DoSummonWave();
                        m_uiEventTimer = urand(60000, 80000);
                        break;
                    default:
                        // The summoning timer of the soldiers isn't very clear
                        DoSummonWave(NPC_SCOURGE_FOOTSOLDIER);
                        m_uiEventTimer = urand(5000, 30000);
                        break;
                    }
                    ++m_uiPhase;
                }
                else
                {
                    m_uiEventTimer -= uiDiff;
                }
            }

            // Handle event end in case of fail
            if (m_uiSadEndTimer)
            {
                if (m_uiSadEndTimer <= uiDiff)
                {
                    DoScriptText(SAY_EVENT_FAIL_2, m_creature);
                    m_creature->ForcedDespawn(5000);
                    DoDespawnSummons();
                    m_uiSadEndTimer = 0;
                    EnterEvadeMode();
                }
                else
                {
                    m_uiSadEndTimer -= uiDiff;
                }
            }
        }
    };

    CreatureAI* GetAI(Creature* pCreature) override
    {
        return new npc_eris_havenfireAI(pCreature);
    }

    bool OnQuestAccept(Player* pPlayer, Creature* pCreature, const Quest* pQuest) override
    {
        if (pQuest->GetQuestId() == QUEST_BALANCE_OF_LIGHT_AND_SHADOW)
        {
            if (npc_eris_havenfireAI* pErisAI = dynamic_cast<npc_eris_havenfireAI*>(pCreature->AI()))
            {
                pErisAI->DoStartBalanceEvent(pPlayer);
                return true;
            }
        }

        return false;
    }
};

void AddSC_eastern_plaguelands()
{
    Script* s;
    s = new npc_eris_havenfire();
    s->RegisterSelf();

    //pNewScript = new Script;
    //pNewScript->Name = "npc_eris_havenfire";
    //pNewScript->GetAI = &GetAI_npc_eris_havenfire;
    //pNewScript->pQuestAcceptNPC = &QuestAccept_npc_eris_havenfire;
    //pNewScript->RegisterSelf();
}
